package com.example.fileupload.service;

import com.example.fileupload.dto.UploadCheckRequest;
import com.example.fileupload.dto.UploadCheckResponse;
import com.example.fileupload.entity.FileInfo;
import com.example.fileupload.mapper.FileInfoMapper;
import com.example.fileupload.storage.StorageStrategy;
import com.example.fileupload.storage.StorageStrategyFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
@Service
public class FileUploadService {

    @Autowired
    private FileInfoMapper fileInfoMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private StorageStrategyFactory storageStrategyFactory;

    @Value("${file.chunk.size:2097152}")
    private long chunkSize; // 2MB

    private static final String REDIS_CHUNK_KEY_PREFIX = "file:";
    private static final String REDIS_CHUNK_KEY_SUFFIX = ":chunks";

    /**
     * 检查文件是否存在（秒传检查）
     */
    public UploadCheckResponse checkFile(UploadCheckRequest request) {
        String md5 = request.getMd5();
        
        // 检查数据库中是否已存在该文件
        if (fileInfoMapper.existsByMd5(md5) > 0) {
            return new UploadCheckResponse(true, "文件已存在，秒传成功");
        }
        
        // 检查是否有未完成的上传
        String redisKey = REDIS_CHUNK_KEY_PREFIX + md5 + REDIS_CHUNK_KEY_SUFFIX;
        Set<Object> uploadedChunks = redisTemplate.opsForSet().members(redisKey);
        
        if (uploadedChunks != null && !uploadedChunks.isEmpty()) {
            List<Integer> chunkList = uploadedChunks.stream()
                    .map(obj -> Integer.parseInt(obj.toString()))
                    .sorted()
                    .collect(Collectors.toList());
            return new UploadCheckResponse(false, "存在未完成的上传", chunkList);
        }
        
        return new UploadCheckResponse(false, "需要上传文件");
    }

    /**
     * 上传文件分片 - 使用存储策略
     */
    public boolean uploadChunk(String md5, int chunkIndex, int totalChunks, MultipartFile chunk) {
        return executeStorageOperation(storage -> {
            String chunkPath = storage.generateChunkPath(md5, chunkIndex);
            
            try (var inputStream = chunk.getInputStream()) {
                StorageStrategy.StorageResult result = storage.storeChunk(chunkPath, inputStream, chunk.getSize());
                
                if (result.isSuccess()) {
                    recordChunkInRedis(md5, chunkIndex);
                    log.info("分片上传成功: {}", chunkPath);
                    return true;
                } else {
                    log.error("分片上传失败: {}, 错误: {}", chunkPath, result.getMessage());
                    return false;
                }
            } catch (Exception e) {
                log.error("分片上传异常: {}, 错误: {}", chunkPath, e.getMessage());
                return false;
            }
        });
    }

    /**
     * 获取已上传的分片列表
     */
    public List<Integer> getUploadedChunks(String md5) {
        String redisKey = REDIS_CHUNK_KEY_PREFIX + md5 + REDIS_CHUNK_KEY_SUFFIX;
        Set<Object> uploadedChunks = redisTemplate.opsForSet().members(redisKey);
        
        if (uploadedChunks == null || uploadedChunks.isEmpty()) {
            return new ArrayList<>();
        }
        
        return uploadedChunks.stream()
                .map(obj -> Integer.parseInt(obj.toString()))
                .sorted()
                .collect(Collectors.toList());
    }

    /**
     * 合并文件分片 - 使用存储策略
     */
    public boolean mergeChunks(String md5, String fileName, int totalChunks, long fileSize) {
        return executeStorageOperation(storage -> {
            // 检查所有分片是否都已上传
            List<Integer> uploadedChunks = getUploadedChunks(md5);
            if (uploadedChunks.size() != totalChunks) {
                return false;
            }

            // 生成分片路径列表
            List<String> chunkPaths = IntStream.range(0, totalChunks)
                    .mapToObj(i -> storage.generateChunkPath(md5, i))
                    .collect(Collectors.toList());

            String finalPath = storage.generateFilePath(fileName, md5);

            StorageStrategy.StorageResult result = storage.mergeChunks(finalPath, chunkPaths, fileSize);
            
            if (result.isSuccess()) {
                saveFileInfo(fileName, md5, result.getPath(), fileSize);
                cleanupChunks(md5, storage);
                log.info("文件合并成功: {}", finalPath);
                return true;
            } else {
                log.error("文件合并失败: {}, 错误: {}", finalPath, result.getMessage());
                return false;
            }
        });
    }



    // ============ 新增的辅助方法 - 函数式风格 ============
    
    /**
     * 执行存储操作的通用模板方法
     */
    private <T> T executeStorageOperation(Function<StorageStrategy, T> operation) {
        try {
            StorageStrategy strategy = storageStrategyFactory.getDefaultStrategy();
            if (strategy == null) {
                log.error("无法获取默认存储策略");
                throw new RuntimeException("无法获取默认存储策略");
            }
            return operation.apply(strategy);
        } catch (Exception e) {
            log.error("执行存储操作失败: {}", e.getMessage(), e);
            throw new RuntimeException("存储操作失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 在Redis中记录分片
     */
    private void recordChunkInRedis(String md5, int chunkIndex) {
        String redisKey = REDIS_CHUNK_KEY_PREFIX + md5 + REDIS_CHUNK_KEY_SUFFIX;
        redisTemplate.opsForSet().add(redisKey, chunkIndex);
        redisTemplate.expire(redisKey, 24 * 60 * 60, java.util.concurrent.TimeUnit.SECONDS);
    }
    
    /**
     * 保存文件信息到数据库
     */
    private void saveFileInfo(String fileName, String md5, String filePath, long fileSize) {
        try {
            FileInfo fileInfo = new FileInfo(fileName, md5, filePath, fileSize);
            fileInfoMapper.insert(fileInfo);
        } catch (Exception e) {
            log.error("保存文件信息失败: {}", e.getMessage());
        }
    }
    
    /**
     * 清理分片文件和Redis缓存 - 使用存储策略
     */
    private void cleanupChunks(String md5, StorageStrategy storage) {
        // 清理Redis缓存
        try {
            String redisKey = REDIS_CHUNK_KEY_PREFIX + md5 + REDIS_CHUNK_KEY_SUFFIX;
            redisTemplate.delete(redisKey);
        } catch (Exception e) {
            log.error("清理Redis缓存失败: {}", e.getMessage());
        }
        
        // 存储策略会自动清理分片文件（在postMergeChunks钩子方法中）
    }
    
    /**
     * 旧版本清理方法（保留兼容性）
     */
    private void cleanupChunks(String md5) {
        String redisKey = REDIS_CHUNK_KEY_PREFIX + md5 + REDIS_CHUNK_KEY_SUFFIX;
        redisTemplate.delete(redisKey);
    }
} 